﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace lighttool.NavMesh
{
    public class navVec3
    {
        public double x;
        public double y;
        public double z;
        public navVec3 clone()
        {
            navVec3 nnn = new navVec3();
            nnn.x = this.x;
            nnn.y = this.y;
            nnn.z = this.z;
            return nnn;
        }
        public static double DistAZ(navVec3 start, navVec3 end)
        {
            double x = end.x - start.x;
            double z = end.z - start.z;
            double len = Math.Sqrt(x * x + z * z);
            return len;

        }
        public static navVec3 NormalAZ(navVec3 start, navVec3 end)
        {
            double x = end.x - start.x;
            double z = end.z - start.z;
            double len = Math.Sqrt(x * x + z * z);
            navVec3 nor = new navVec3();
            nor.x = x / len;
            nor.y = 0;
            nor.z = z / len;
            return nor;
        }
        public static navVec3 Cross(navVec3 start, navVec3 end)
        {
            navVec3 cro = new navVec3();
            cro.x = start.y * end.z - start.z * end.y;
            cro.y = start.z * end.x - start.x * end.z;
            cro.z = start.x * end.y - start.y * end.x;
            return cro;
        }
        public static double DotAZ(navVec3 start, navVec3 end)
        {
            var dot = start.x * end.x + start.z * end.z;
            return dot;
        }
        public static double Angle(navVec3 start, navVec3 end)
        {
            var dot = start.x * end.x + start.z * end.z;
            var cross = navVec3.Cross(start, end);
            double angle = Math.Acos(dot);
            if (cross.y < 0)
                angle = -angle;
            return angle;
        }
        public static navVec3 Border(navVec3 start, navVec3 end, float dist)
        {
            var n = navVec3.NormalAZ(start, end);
            navVec3 v = new navVec3();
            v.x = start.x + n.x * dist;
            v.y = start.y + n.y * dist;
            v.z = start.z + n.z * dist;
            return v;
        }
    }
    public class navNode
    {
        public int nodeID;
        public int[] poly;//多边形
        public void genBorder()
        {
            List<string> _bb = new List<string>();
            for (int i = 0; i < poly.Length; i++)
            {
                int i0 = i;
                int i1 = i + 1;
                if (i1 >= poly.Length) i1 = 0;

                int b1 = poly[i0];
                int b2 = poly[i1];
                if (b1 < b2)
                    _bb.Add(b1 + "-" + b2);
                else
                    _bb.Add(b2 + "-" + b1);

            }
            this.borderByPoint = _bb.ToArray();
        }
        public string[] borderByPoly;//形状对应的边
        public string[] borderByPoint;//节点对应的边

        public navVec3 center;
        //判断两个节点是否相连
        public string isLinkTo(navMeshInfo info, int nid)
        {
            if (nodeID == nid)
                return null;
            if (nid < 0)
                return null;
            foreach (var b in borderByPoly)
            {
                if (info.borders.ContainsKey(b) == false)
                    continue;
                if (info.borders[b].nodeA == nid || info.borders[b].nodeB == nid)
                    return b;
            }
            return null;
        }
        //获取连接节点
        public int[] getLinked(navMeshInfo info)
        {
            List<int> lnode = new List<int>();
            foreach (var b in borderByPoly)
            {
                int onode = -1;
                if (info.borders.ContainsKey(b) == false)
                    continue;

                if (info.borders[b].nodeA == this.nodeID)
                    onode = info.borders[b].nodeB;
                else
                    onode = info.borders[b].nodeA;
                if (onode >= 0)
                    lnode.Add(onode);
            }
            return lnode.ToArray();
        }
        public void genCenter(navMeshInfo info)
        {
            this.center = new navVec3();
            this.center.x = 0;
            this.center.y = 0;
            this.center.z = 0;
            foreach (var p in poly)
            {
                this.center.x += info.vecs[p].x;
                this.center.y += info.vecs[p].y;
                this.center.z += info.vecs[p].z;
            }
            this.center.x /= poly.Length;
            this.center.y /= poly.Length;
            this.center.z /= poly.Length;
        }
    }
    public class navBorder
    {
        public string borderName;
        public int nodeA;//边 对应的 节点
        public int nodeB;//边 对应的 节点

        public int pointA;
        public int pointB;

        public float length;
        public navVec3 center;
    }
    public class navMeshInfo
    {
        public navVec3[] vecs;//顶点
        public navNode[] nodes;//节点
        public Dictionary<string, navBorder> borders;//边
        public navVec3 min;
        public navVec3 max;
        public void calcBound()
        {
            min = new navVec3();
            max = new navVec3();
            min.x = double.MaxValue;
            min.y = double.MaxValue;
            min.z = double.MaxValue;
            max.x = double.MinValue;
            max.y = double.MinValue;
            max.z = double.MinValue;
            for (var i = 0; i < vecs.Length; i++)
            {
                if (vecs[i].x < min.x) min.x = vecs[i].x;
                if (vecs[i].y < min.y) min.y = vecs[i].y;
                if (vecs[i].z < min.z) min.z = vecs[i].z;
                if (vecs[i].x > max.x) max.x = vecs[i].x;
                if (vecs[i].y > max.y) max.y = vecs[i].y;
                if (vecs[i].z > max.z) max.z = vecs[i].z;
            }

        }
        static double cross(navVec3 p0, navVec3 p1, navVec3 p2)
        {
            return (p1.x - p0.x) * (p2.z - p0.z) - (p2.x - p0.x) * (p1.z - p0.z);
        }
        public bool inPoly(navVec3 p, int[] poly)
        {
            float eps = 0f;
            if (poly.Length < 3) return false;
            if (cross(this.vecs[poly[0]], p, this.vecs[poly[1]]) < -eps) return false;
            if (cross(this.vecs[poly[0]], p, this.vecs[poly[poly.Length - 1]]) > eps) return false;

            int i = 2, j = poly.Length - 1;
            int line = -1;

            while (i <= j)
            {
                int mid = (i + j) >> 1;
                if (cross(this.vecs[poly[0]], p, this.vecs[poly[mid]]) < -eps)
                {
                    line = mid;
                    j = mid - 1;
                }
                else i = mid + 1;
            }
            double r = cross(this.vecs[poly[line - 1]], p, this.vecs[poly[line]]);
            return r > eps;
        }
        public void genBorder()
        {
            var __borders = new Dictionary<string, navBorder>();
            foreach (var n in nodes)
            {
                foreach (var b in n.borderByPoint)
                {
                    if (__borders.ContainsKey(b) == false)
                    {
                        __borders[b] = new navBorder();
                        __borders[b].borderName = b;
                        __borders[b].nodeA = n.nodeID;
                        __borders[b].nodeB = -1;
                        __borders[b].pointA = -1;
                        //这个错了，边的中点
                    }
                    else
                    {

                        __borders[b].nodeB = n.nodeID;
                        if (__borders[b].nodeA > __borders[b].nodeB)
                        {
                            __borders[b].nodeB = __borders[b].nodeA;
                            __borders[b].nodeB = n.nodeID;
                        }
                        var na = this.nodes[__borders[b].nodeA];
                        var nb = this.nodes[__borders[b].nodeB];
                        foreach (int i in na.poly)
                        {
                            if (nb.poly.Contains(i))
                            {
                                if (__borders[b].pointA == -1)

                                    __borders[b].pointA = i;
                                else
                                    __borders[b].pointB = i;
                            }
                        }
                        var left = __borders[b].pointA;
                        var right = __borders[b].pointB;

                        var xd = this.vecs[left].x - this.vecs[right].x;
                        var yd = this.vecs[left].y - this.vecs[right].y;
                        var zd = this.vecs[left].z - this.vecs[right].z;

                        __borders[b].length = (float)Math.Sqrt(xd * xd + yd * yd + zd * zd);
                        __borders[b].center = new navVec3();
                        __borders[b].center.x = this.vecs[left].x * 0.5 + this.vecs[right].x * 0.5;
                        __borders[b].center.y = this.vecs[left].y * 0.5 + this.vecs[right].y * 0.5;
                        __borders[b].center.z = this.vecs[left].z * 0.5 + this.vecs[right].z * 0.5;
                        __borders[b].borderName = __borders[b].nodeA + "-" + __borders[b].nodeB;
                    }

                }

            }
            Dictionary<string, string> namechange = new Dictionary<string, string>();
            foreach (var b in __borders.Keys.ToArray())
            {
                if (__borders[b].nodeB < 0)
                {
                }
                else
                {
                    namechange[b] = __borders[b].borderName;
                }
            }
            this.borders = new Dictionary<string, navBorder>();
            foreach (var b in __borders)
            {
                if (namechange.ContainsKey(b.Key))
                {
                    this.borders[namechange[b.Key]] = b.Value;
                }
            }

            foreach (var v in nodes)
            {
                List<string> newborder = new List<string>();
                foreach (var b in v.borderByPoint)
                {
                    if (namechange.ContainsKey(b))
                    {
                        newborder.Add(namechange[b]);
                    }
                }
                v.borderByPoly = newborder.ToArray();
            }
        }
    }
}